package org.coderjonny.aop;

import com.alibaba.fastjson2.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.coderjonny.log.LogEntity;
import org.coderjonny.thread.ThreadLocalUtil;
import org.coderjonny.trace.TraceIdUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.Parameter;

@Component
@Aspect
public class ControllerLogAspect {

    @Value("${spring.application.name}")
    private String application;
    private static final Logger LOGGER = LoggerFactory.getLogger(ControllerLogAspect.class);


    @Pointcut("execution(public * org.coderjonny.controller.*Controller..*(..))")
    public void pointcut(){
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        LogEntity logEntity = (LogEntity) ThreadLocalUtil.get(ThreadLocalUtil.LOG_ENTITY);
        if (logEntity == null){
            String traceId = TraceIdUtil.newTraceId();
            logEntity = new LogEntity();
            logEntity.setTraceId(traceId);
            logEntity.setApplication(application);
            ThreadLocalUtil.set(ThreadLocalUtil.LOG_ENTITY,logEntity);
            MDC.put(TraceIdUtil.SLEUTH_TRACE_NAME,traceId);
        }
        Object result = null;
        Object[] args = null;
        Throwable throwable = null;
        String clazz = joinPoint.getSignature().getDeclaringType().getSimpleName();
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        String method = joinPoint.getSignature().getName();
        try{
            args = joinPoint.getArgs();
            result = joinPoint.proceed(args);
            return result;
        } catch (Throwable e) {
            throwable = e;
            throw e;
        } finally {
            JSONObject argsObject = parseArgument(signature,args);
            if (logEntity.getRequest() == null){
                logEntity.setRequest(argsObject);
            }
            logEntity.setClazz(clazz);
            logEntity.setMethod(method);
            logEntity.setThrowable(throwable);
        }
    }


    private JSONObject parseArgument(MethodSignature signature,Object[] args){
        JSONObject argsObject = new JSONObject();
        Parameter[] parameters = signature.getMethod().getParameters();
        if (parameters == null || parameters.length == 0){
            return argsObject;
        }
        for (int i = 0; i < parameters.length; i++) {
            argsObject.put(parameters[i].getName(), args[i]);
        }
        return argsObject;
    }


}
